package com.service.atf.base.core;


import com.service.atf.base.exception.TFException;
import com.service.atf.base.type.*;
import com.service.atf.base.utils.ATFConstants;
import com.service.atf.base.utils.ATFLogger;
import com.luotao.devp.base.db.Pager;
import com.luotao.devp.base.utils.ByteUtil;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;


/**
 * =============================================================================<br>
 * 类型名称: XFilePoolWriter<br>
 * 类型版本: V1.0<br>
 * 类型描述: 文件池类操作基础类 <br>
 * 类型作者: Dennis <br>
 * 创建日期: 2010-12-24 <br>
 * 类型备注: <br>
 * =============================================================================<br>
 * 修改日期 修改前版本 修改后版本 修改人 备注<br>
 * =============================================================================
 */
@SuppressWarnings("unused")
public class TFCheckPointReader implements Runnable {


    private BuffReaderHead buffAdderHead;

    private BuffWriterHead buffReadAndWriterHead;

    private BuffWriterHead buffCheckPoint;

    private MappedByteBuffer buffBody;

    //操作文件句柄
    private FileChannel channel;
    // 交换文件文件访问句柄
    private RandomAccessFile accfile;

    private Pager pager = new Pager();

    public TFCheckPointReader(String filePath, int pageSize) {
        try {
            //设置分页信息
            pager.setPageSize(pageSize);
            pager.setTotalCount(ATFConstants.TF_LEN_BODY);
            accfile = new RandomAccessFile(filePath, "rw");
            channel = accfile.getChannel();
            buffAdderHead = new BuffReaderHead(channel, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * @return 返回0表示已经追赶到写指针，返回1表示业务已经处理完成
     */
    private List<CheckpointInfo> searchCheckpoint() {
        List<CheckpointInfo> checks = new ArrayList<CheckpointInfo>();
//    checks.add(findFileStart());
        int offset = 0;
        changeBody();
        while (offset <= buffAdderHead.read()) {
            //读取数据的长度
            short len = getRecordLen();


            //读取数据的内容
            byte[] data = readRecData(len, buffBody.remaining());

            if (data.length < 10) continue;
            char type = (char) data[8];
            char tradeType = (char) data[9];
            if (type != '2' || (tradeType != '1' && tradeType != '2')) {
                offset += len + 2;
                continue;
            }
            //获得checkpoint点中存储的文件名称
            byte[] filename = new byte[50];
            System.arraycopy(data, 14, filename, 0, filename.length);
            String str_filename = new String(filename);
            CheckpointInfo checkpointInfo = new CheckpointInfo(offset, str_filename);
            if (tradeType == '1') {
                checkpointInfo.setLenWithDataOffset(GlobalDefine.TF_LEN_CHECKPOINT);
            } else {
                checkpointInfo.setLenWithDataOffset(GlobalDefine.TF_LEN_START);
            }
            checks.add(checkpointInfo);
            offset += len + 2;
        }
        return checks;
    }

    public CheckpointInfo findValidCheckpoint() {
        List<CheckpointInfo> infoList = searchCheckpoint();
        for (int i = infoList.size() - 1; i >= 0; i--) {
            CheckpointInfo checkpointInfo = infoList.get(i);
            //读取内存快照的数据
            ATFLogger.sysdebug("checkpoint path : " + checkpointInfo.getFilename());
            byte[] dataInFile = NIOFileReader.readFileContent(checkpointInfo.getFilename(), ATFConstants.AC_USER_LEN_ALL + 8 + 1);
            //检查此文件内容是否有效
            char valid = (char) dataInFile[dataInFile.length - 1];
            if (valid != '1') {
                continue;
            }
            return checkpointInfo;
        }
        baseClose();
        return null;
    }

    private CheckpointInfo findFileStart() {
        MappedByteBuffer buffWriterCommon = BuffWriterCommon.createReadBuff(channel, GlobalDefine.TF_HEAD_LEN_ALL
                , GlobalDefine.TF_LEN_START);
        byte[] filename = new byte[50];
        buffWriterCommon.position(8);
        buffWriterCommon.get(filename);
        CheckpointInfo info = new CheckpointInfo(-1, new String(filename));
        return info;
    }

    private byte[] readRecData(int len, int remain) {
        //如果内存里面的量足够读取本条记录，则直接读取
        if (remain >= len) {
            byte[] data = new byte[len];
            buffBody.get(data);
            return data;
        }
        //否则分2次读取
        else {
            byte[] b1 = new byte[remain];
            buffBody.get(b1);
            byte[] b2 = new byte[len - remain];
            pager.setPageNo(pager.getPageNo() + 1);
            changeBody();
            buffBody.get(b2);
            byte[] result = new byte[len];
            System.arraycopy(b1, 0, result, 0, b1.length);
            System.arraycopy(b2, 0, result, b1.length, b2.length);
            return result;
        }
    }

    private short getRecordLen() {
        short len;
        if (buffBody.remaining() == 1) {
            byte[] bb = new byte[2];
            bb[0] = buffBody.get();
            pager.setPageNo(pager.getPageNo() + 1);
            changeBody();
            bb[1] = buffBody.get();
            len = (short) ByteUtil.readShort(bb, 0);
        } else if (buffBody.remaining() > 1) {
            len = buffBody.getShort();
        } else {
            pager.setPageNo(pager.getPageNo() + 1);
            changeBody();
            len = buffBody.getShort();
        }
        return len;
    }

    public void changeBody() {
        try {
            buffBody = channel.map(FileChannel.MapMode.READ_ONLY, GlobalDefine.TF_HEAD_LEN_ALL + pager.getFirst() - 1, pager.getPageSize());
        } catch (IOException e) {
            String msg = "TFReader中切换内存发生错误 " + e.toString();
            ATFLogger.syserr(msg);
            throw new TFException(ATFConstants.ATF_TF_MEM_CHANGE_ERR, msg);
        }

    }


    /**
     * 关闭文件池
     */
    public boolean baseClose() {
        try {
            if (accfile != null) {
                accfile.close();
            }
            if (channel != null) {
                channel.close();
            }
            return true;
        } catch (Throwable th) {
            return false;
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                searchCheckpoint();
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public byte[] getDataByOffset(int offset) {
        BuffReaderBodyLen buffDataLen = new BuffReaderBodyLen(channel, offset);
        int recLen = buffDataLen.read();
        BuffReaderBody buffReaderBody = new BuffReaderBody(channel
                , offset + GlobalDefine.TF_RECORD_LEN_BYTE, recLen);
        return buffReaderBody.readAllData();
    }

    public byte[] getCkeckpointData() {

        try {
            MappedByteBuffer buffData = channel.map(FileChannel.MapMode.READ_ONLY
                    , GlobalDefine.TF_HEAD_LEN_ALL, GlobalDefine.TF_LEN_START);
            buffData.getShort();
            byte[] res = new byte[buffData.remaining()];
            buffData.get(res);
            return res;
        } catch (Exception e) {
            return null;
        }

    }

    public int doBusiness(byte[] data, int offset) {
        System.out.println("in reader offset = " + offset);
        String s = new String(data);
        System.out.println("in reader s = " + s);
        return 0;
    }
}

